var H5R = function(){

	var stages = {};
	var renderTO;
	var timer;
	var timePassed = 0;
	var running = false;
	var tileset = {}; //raw tileset reference
	var images = {}; //images loaded from tileset
	var SG = {}; //scenegraph
	
	var init = function(){
		restart();
		
		MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
		if(MutationObserver){
			var observer = new MutationObserver(observe);
			observer.observe(document, {
				subtree: true,
				attributes: true,
				childList: true,
			});
		}
	};
	
	var observe = function(mutations, observer){
		console.log("change noticed",mutations);
		updateStages();
	};
	
	var updateStages = function(){
		for(var i in stages){
			stages[i].checkDOMElement();
		}
	};
	
	var stop = function(){
		running = false;
		if(renderTO){
			clearTimeout(renderTO);
			renderTO = null;
		}
	};
	
	var restart = function(){
		stop();
		running = true;
		timer = new Date().getTime();
		timePassed = 0;
		renderStages();
	};
	
	var renderStages = function(){
		var newTime = new Date().getTime();
		var timeDiff = newTime - timer;
		timer = newTime;
		timePassed += timeDiff;
	
		updateSG(SG, timeDiff, timePassed);
		
		for(var i in stages){
			stages[i].render(SG, timeDiff, timePassed, {getImage: getImage});
		}
	
		if(running) renderTO = setTimeout(renderStages, 1);
	};
	
	var updateSG = function(sgObj, timeDiff, timePassed){
		if(sgObj instanceof Object){
			if(sgObj.update) sgObj.update(sgObj, timeDiff, timePassed);
			if(sgObj.fps){
				if(!sgObj.startFrame) sgObj.startFrame = sgObj.frame || 0;
				sgObj.frame = Math.round(timePassed * (sgObj.fps/1000)) + sgObj.startFrame;
			}
		}
		if(sgObj instanceof Object || sgObj instanceof Array){
			for(var i in sgObj){
				updateSG(sgObj[i], timeDiff, timePassed);
			}
		}
	};
	
	var addStage = function(stage){
		if(!stage || !(stage instanceof H5R.Stage) || !stage.isValid()){
			console.error("The submitted stage is not a valid H5R.Stage object!");
		} else {
			stages[stage.getId()] = stage;
		}
	};
	
	var getStage = function(id){
		return stages[id] || null;
	};
	
	var removeStage = function(id){
		delete stages[id];
	};
	
	var setTileset = function(ts, cb){
		if(!ts){
			console.error("Each tileset object is invalid!");
		} else {
			tileset = ts;
			updateTileset(cb);
		}
	};
	
	var updateTileset = function(cb){
		cb = cb || function(){};
		images = {};
		readTileset(tileset);
		createImages(cb);
		console.log(images);
	};
	
	var createImages = function(cb){
		var indices = [];
		for(var i in images){
			indices.push(i);
		}
		loadImage(indices, 0, 0, cb);
	};
	
	var loadImage = function(indices, i, j, cb){
		if(i < indices.length){
			if(j < images[indices[i]].length){
				if(images[indices[i]][j].sprites <= 0) images[indices[i]][j].sprites = 1;
				var img = new Image();
				img.onload = function(){
					images[indices[i]][j].img = img;
					images[indices[i]][j].width = img.width / images[indices[i]][j].sprites;
					images[indices[i]][j].height = img.height;
					loadImage(indices, i, j+1, cb);
				};
				img.onerror = function(){
					loadImage(indices, i, j+1, cb);
				};
				img.src = images[indices[i]][j].src;
			} else {
				loadImage(indices, i+1, 0, cb);
			}
		} else {
			cb();
		}
	};
	
	var readTileset = function(tsObj, path){
		path = path || "";
		for(var i in tsObj){
			if(i.toLowerCase() == "img"){
				readImages(tsObj[i], path);
			} else if(i.toLowerCase() == "src" && 
					(typeof tsObj[i]).toLowerCase() == "string"){
				readImages(tsObj[i], path);
			} else {
				if(path != ""){
					readTileset(tsObj[i], path + "." + i);
				} else {
					readTileset(tsObj[i], i);
				}
			}
		}
	};
	
	var readImages = function(imgObj, path){
		images[path] = [];
		if(imgObj instanceof Array){
			for(var i in imgObj){
				if(imgObj[i].src){
					images[path].push({
						src: imgObj[i].src,
						sprites: imgObj[i].sprites || 1,
					});
				} else if((typeof imgObj[i]).toLowerCase() == "string"){
					images[path].push({
						src: imgObj[i],
						sprites: 1,
					});
				}
			}
		} else if(imgObj instanceof Object){
			if(imgObj.src){
				images[path].push({
					src: imgObj.src,
					sprites: imgObj.sprites || 1,
				});
			}
		} else {
			images[path].push({
				src: imgObj,
				sprites: 1,
			});
		}
	};
	
	var getImage = function(id, animation){
		id = id || "";
		animation = animation || 0;
		if(images[id] && images[id].length > animation){
			return images[id][animation];
		} else {
			//TODO image not found
			return null;
		}
	};
	
	var setScenegraph = function(scenegraph){
		if(!scenegraph){
			console.error("The scene graph object is invalid!");
		} else {
			SG = scenegraph;
		}
	};
	
	
	init();
	
	this.addStage		= addStage;
	this.getStage		= getStage;
	this.removeStage	= removeStage;
	this.stop			= stop;
	this.restart		= restart;
	this.start			= restart;
	this.setTileset		= setTileset;
	this.updateTileset	= updateTileset;
	this.getImage		= getImage;
	this.updateStages	= updateStages;
	this.setScenegraph	= setScenegraph;
};

H5R.Stage = function(id, options){
	
	var visible = false;
	var elementId;
	var valid = false;
	var canvas;
	var ctx;
	var params;
	var sgPart = "";
		
	var isValid = function(){
		return valid;
	};
	
	var render = function(SG, timeDiff, timePassed, renderer){
		if(visible){
			renderSG(SG, timeDiff, timePassed, renderer);
		}
	};
	
	var renderSG = function(SG, timeDiff, timePassed, renderer){
		ctx.clearRect(0, 0, canvas.width, canvas.height);
		
		if(sgPart == ""){
			renderSGPart(SG, timeDiff, timePassed, renderer);
		} else {
			var sgRenderPart = SG;
			var splitPart = sgPart.split(".");
			for(var i in splitPart){
				if (sgRenderPart instanceof Object){
					if(sgRenderPart[splitPart[i]]){
						sgRenderPart = sgRenderPart[splitPart[i]];
					} else {
						return;
					}
				} else if(sgRenderPart instanceof Array){
					var idx = splitPart[i]-0;
					if(sgRenderPart[idx]){
						sgRenderPart = sgRenderPart[idx];
					} else {
						return;
					}
				} else {
					return;
				}
			}
			renderSGPart(sgRenderPart, timeDiff, timePassed, renderer);
		}
	};
	
	var renderSGPart = function(sgObj, timeDiff, timePassed, renderer){
		if(sgObj){
			/*if(sgObj.fps){
				if(!sgObj.startFrame) sgObj.startFrame = sgObj.frame || 0;
				sgObj.frame = Math.round(timePassed * (sgObj.fps/1000)) + sgObj.startFrame;
			}*/
			
			//if(sgObj.update) sgObj.update(sgObj, timeDiff, timePassed); //(self, timediff, timepassed)
			
			var scaleX = sgObj.scaleX || sgObj.scale || 1;
			if(scaleX == 0) scaleX = 1;
			var scaleY = sgObj.scaleY || sgObj.scale || 1;
			if(scaleY == 0) scaleY = 1;
			var x = sgObj.x || 0;
			var y = sgObj.y || 0;
					
			ctx.translate(x, y);
			ctx.scale(scaleX, scaleY);
			ctx.save();
			
			if(sgObj.img){
				
				var animation = sgObj.animation || 0;
				var img = renderer.getImage(sgObj.img, animation);
							
				if(img && img.img){
					var width = sgObj.width || -1;
					if(width < 0 || width > img.width) width = img.width;
					var height = sgObj.height || -1;
					if(height < 0 || height > img.height) height = img.height;
					
					var frame = sgObj.frame || 0;
					while(frame < 0) frame += img.sprites;
					if(frame >= img.sprites) frame = frame % img.sprites;
					
					ctx.drawImage(
							img.img, //img obj
							img.width * frame, //src x
							0, //src y
							width, //src width 
							height, //src height
							0, //dest x
							0, //dest y
							width, //dest width
							height //dest height
					);
				}
			
			}
						
			if(sgObj.render) sgObj.render(sgObj, ctx);//(self, context)
			/*if(sgObj.children){
				for(var i in sgObj.children){
					renderSGPart(sgObj.children[i], timeDiff, renderer);
				}
			}*/
			for(var i in sgObj){
				if(sgObj instanceof Object || sgObj instanceof Array){
					renderSGPart(sgObj[i], timeDiff, timePassed, renderer);
				}
			}
			
			ctx.restore();
			ctx.scale(1/scaleX, 1/scaleY);
			ctx.translate(-x, -y);
		
		}
	}
		
	var checkDOMElement = function(){
		var elem = document.getElementById(elementId);
		if(elem && (elem.offsetWidth > 0 || elem.offsetHeight > 0) && (elem.style.opacity == "" || elem.style.opacity > 0)){
			if(!visible){
				visible = true;
				initDOMElement(elem);
			}
		} else {
			visible = false;
		}
	};
	
	var initDOMElement = function(elem){
		elem.innerHTML = "";
		canvas = document.createElement("canvas");
		elem.appendChild(canvas);
		console.log(elem);
		canvas.width = params.width || elem.clientWidth;
		canvas.height = params.height || elem.clientHeight;
		ctx = canvas.getContext('2d');
	};
	
	var getId = function(){
		return elementId;
	};
	
	var setScenegraphPart = function(part){
		if(!part || typeof part != "string"){
			console.error("Each H5R.Stage needs a valid id!");
		} else {
			sgPart = part;
		}
	};
	
	var init = function(id, options){
		if(!id || id.trim() == ""){
			console.error("Each H5R.Stage needs a valid id!");
		} else {
			valid = true;
			params = {};
			if(options){
				if(options.width) params.width = options.width;
				if(options.height) params.height = options.height;
			}
			elementId = id;
			checkDOMElement();
		}
	};
	
	init(id, options);
	
	this.render				= render;
	this.checkDOMElement	= checkDOMElement;
	this.isValid			= isValid;
	this.getId				= getId;
	this.setScenegraphPart	= setScenegraphPart;
	
};